package com.lagou.edu.factory;

import com.lagou.edu.annotation.*;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.File;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author 应癫
 *
 * 工厂类，生产对象（使用反射技术）
 */
public class BeanFactory {

    /**
     * 任务一：读取解析xml，通过反射技术实例化对象并且存储待用（map集合）
     * 任务二：对外提供获取实例对象的接口（根据id获取）
     */

    private static Map<String,Object> map = new HashMap<>();  // 存储对象
    static List<String> classNames = new ArrayList<>();  // 存储注解扫描范围内所有className
    static List<String> fieldsAlreayProcessed = new ArrayList<>();  //缓存已经进行过依赖注入的信息


    static {
        // 任务一：读取解析xml，通过反射技术实例化对象并且存储待用（map集合）
        // 加载xml
        InputStream resourceAsStream = BeanFactory.class.getClassLoader().getResourceAsStream("beans.xml");
        // 解析xml
        SAXReader saxReader = new SAXReader();
        try {
            Document document = saxReader.read(resourceAsStream);
            Element rootElement = document.getRootElement();
            List<Element> beanList = rootElement.selectNodes("//company-scan");
            for (int i = 0; i < beanList.size(); i++) {
                Element element =  beanList.get(i);
                //扫描注解的范围
                String basePackage = element.attributeValue("base-package");
                //查找@MyComponent、@MyService、@MyRepository、@MyAutowired注解标注的class全限定类名,放入beanNameMap
                doScan(basePackage);

                // 通过反射技术实例化对象
                doInstance();

                //维护对象的依赖关系
                doAutoWired();
                System.out.println("结束map"+map);
                System.out.println("结束classNames"+classNames);
                System.out.println("结束fieldsAlreayProcessed"+fieldsAlreayProcessed);

                //事物控制
                doTransactional();

            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static void doTransactional() {
        ProxyFactory proxyFactory = (ProxyFactory) map.get("proxyFactory");  //获取到动态代理类
        for (Map.Entry<String, Object> stringObjectEntry : map.entrySet()) {
            Class<?> aClass = stringObjectEntry.getValue().getClass();
            //如果有@MyTransaction标识 ，则执行事物动态代理
            if(aClass.isAnnotationPresent(MyTransaction.class)){
                String key = stringObjectEntry.getKey();
                Object value = stringObjectEntry.getValue();
                Class<?>[] interfaces = aClass.getInterfaces();
                if(interfaces != null && interfaces.length > 0) {
                    // 使用jdk动态代理
                    map.put(key,proxyFactory.getJdkProxy(value));
                }else{
                    // 使用cglib动态代理
                    map.put(key,proxyFactory.getCglibProxy(value));
                }
                System.out.println("动态代理key"+key);
                System.out.println("动态代理map"+map);
            }
        }
    }

    private static void doAutoWired() throws IllegalAccessException {
        // 遍历ioc中所有对象，查看对象中的字段，是否有@MyAutowired注解，如果有需要维护依赖注入关系
        for (Map.Entry<String, Object> stringObjectEntry : map.entrySet()) {
            doObjectDependancy(stringObjectEntry.getValue());
        }
    }

    private static void doObjectDependancy(Object object) throws IllegalAccessException {
        Field[] declaredFields = object.getClass().getDeclaredFields();  //获取类所有字段
        if(declaredFields == null || declaredFields.length==0){
            return;
        }
        for (Field declaredField : declaredFields) {  //遍历字段

            //如果没有被@MyAutowired注解标识
            if(!declaredField.isAnnotationPresent(MyAutowired.class)){
                continue;
            }
            //如果当前字段是有处理过（依赖注入map中是否包含该对象.属性名），避免嵌套处理死循环
            if(fieldsAlreayProcessed.contains(object.getClass().getName()  + "." + declaredField.getName())){
                continue;
            }
            //依赖对象获取：先按照属性上注解配置的value获取依赖的对象，如果获取不到再按照首字母小写
            Object dependObject = null;
            dependObject = map.get(declaredField.getType().getName());
            if(dependObject == null) {
                dependObject = map.get(lowerFirst(declaredField.getType().getSimpleName()));
            }
            // 记录下给哪个对象的哪个属性设置过，避免死循环
            fieldsAlreayProcessed.add(object.getClass().getName() + "." + declaredField.getName());
            // 迭代
             doObjectDependancy(dependObject);
            //把处理之后的stringObjectEntry重新放到map中
            declaredField.setAccessible(true);
            declaredField.set(object,dependObject);
        }
    }



    private static void doObjectDependancy1(Object object) throws IllegalAccessException {
        Field[] declaredFields = object.getClass().getDeclaredFields();

        if(declaredFields == null || declaredFields.length ==0) {
            return;
        }
        // 遍历判断处理
        for (int i = 0; i < declaredFields.length; i++) {

            Field declaredField = declaredFields[i];

            if (!declaredField.isAnnotationPresent(MyAutowired.class)) {
                continue;
            }


            // 判断当前字段是否处理过，如果已经处理过则continue，避免嵌套处理死循环
            if(fieldsAlreayProcessed.contains(object.getClass().getName()  + "." + declaredField.getName())){
                continue;
            }


            Object dependObject = null;
            dependObject = map.get(declaredField.getType().getName());  //  先按照声明的是接口去获取，如果获取不到再按照首字母小写

            if(dependObject == null) {
                dependObject = map.get(lowerFirst(declaredField.getType().getSimpleName()));
            }

            // 记录下给哪个对象的哪个属性设置过，避免死循环
            fieldsAlreayProcessed.add(object.getClass().getName() + "." + declaredField.getName());

            // 迭代
            doObjectDependancy1(dependObject);

            declaredField.setAccessible(true);
            declaredField.set(object,dependObject);

        }
    }




    private static void doInstance(){
        try {
            for (String className : classNames) {
                Class<?> aClass = Class.forName(className);
                if(aClass.isAnnotationPresent(MyService.class) || aClass.isAnnotationPresent(MyComponent.class) || aClass.isAnnotationPresent(MyRepository.class)){
                    //有@MyAutowired、@MyComponent、@MyRepository注解标识的，进行实例化，将bean对象存入map中 --> key:用户设置了注解value的话，就以指定的为准，否则用class的默认的简写名称
                    String beanName = null;
                    Object o = aClass.newInstance();  //实例化对象
                    //获取注解中设置的value，若有设置value，则以用户设置为准
                    if (aClass.isAnnotationPresent(MyService.class)) {
                        beanName = aClass.getAnnotation(MyService.class).value();
                    } else if (aClass.isAnnotationPresent(MyRepository.class)) {
                        beanName = aClass.getAnnotation(MyRepository.class).value();
                    } else if (aClass.isAnnotationPresent(MyComponent.class)) {
                        beanName = aClass.getAnnotation(MyComponent.class).value();
                    }
                    if ("".equals(beanName.trim())) {  //去掉两端的多余的空格
                        beanName = lowerFirst(aClass.getSimpleName());  //如果注解value值为空，则获取该class的默认的简写名称（修改首字母为小写）
                    }
                    System.out.println("map->key---------------"+beanName);
                    map.put(beanName,o);  //放到容器map中，缓存

                    // service层接口处理：面向接口开发，此时再以接口名为id，放入一份对象到容器中，便于后期根据接口类型注入
                    Class<?>[] interfaces = aClass.getInterfaces();
                    if(interfaces != null && interfaces.length > 0) {
                        for (Class<?> anInterface : interfaces) {
                            System.out.println("接口map->key---------------"+anInterface.getName());
                            map.put(anInterface.getName(), aClass.newInstance());
                        }
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    /**
     * 首字母小写方法
     */
    private static String lowerFirst(String str) {
        char[] chars = str.toCharArray();
        if('A' <= chars[0] && chars[0] <= 'Z') {
            chars[0] += 32;
        }
        return String.valueOf(chars);
    }

    private static void doScan(String basePackage) {
        //注解扫描范围的范围（ClassPath的绝对URI路径）
        String scanPackagePath = Thread.currentThread().getContextClassLoader().getResource("").getPath() + basePackage.replaceAll("\\.", "/");
        File pack = new File(scanPackagePath);
        //获取com.lagou.edu路径下第一层所有的文件（或文件夹）
        File[] files = pack.listFiles();
        for(File file: files) {
            if(file.isDirectory()) {  // 若是文件夹（子package），递归
                doScan(basePackage + "." + file.getName());  // com.lagou.demo.controller
            }else if(file.getName().endsWith(".class")) {
                String className = basePackage + "." + file.getName().replaceAll(".class", "");
                classNames.add(className);  //将所有class文件全限定类名放入缓存
            }
        }
    }


    // 任务二：对外提供获取实例对象的接口（根据id获取）
    public static  Object getBean(String id) {
        return map.get(id);
    }

}
